package com.jacky.flutter_screen_recording;

import android.annotation.SuppressLint;
import android.app.Dialog;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.projection.MediaProjection;
import android.net.ConnectivityManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.WindowManager;

import com.enotary.mobile.screen.FaXinServiceConnection;
import com.enotary.mobile.screen.IFaXinScreenCallback;
import com.enotary.mobile.screen.IFaXinScreenObserver;
import com.jacky.log.Logger;
import com.jacky.path_provider_ex.*;
import com.meituan.flutter_logan.*;
import com.jacky.media.MediaRecorderProxy;

import java.io.File;
import java.io.IOException;
import java.util.Locale;

import static com.enotary.mobile.screen.FaXinServiceConnection.CHECK_FAIL;
import static com.enotary.mobile.screen.FaXinServiceConnection.ERR_DURATION;
import static com.enotary.mobile.screen.FaXinServiceConnection.ERR_INIT;
import static com.enotary.mobile.screen.FaXinServiceConnection.LOW_BATTERY;
import static com.enotary.mobile.screen.FaXinServiceConnection.SCREEN_Err;
import static com.enotary.mobile.screen.FaXinServiceConnection.SCREEN_RECORDING;

import org.jetbrains.annotations.Nullable;
import org.json.JSONStringer;

public class ScreenRecordService extends Service {

    private final IFaXinScreenObserver.Stub mStub = new IFaXinScreenObserver.Stub() {

        @Override
        public int initScreen(String packageName, String dirName, int maxDuration, long serverTime,
                              boolean latlngTrack, boolean ignoreSim, int micMode, String fileDigest) {
            String s = String.format(Locale.CHINESE,"Dir=%s, Duration=%d, Time=%d, Mic=%d",dirName, maxDuration, serverTime, micMode);
            Logger.i(s);
            FlutterLoganPlugin.w(s,3);
            if (isRecordRunning) {//此处只能用 isRecordRunning， 不能用其他函数代替,这个逻辑是用来判断是否在运行录屏，不区分那个应用
                //同一个录屏的app
                try {
                    if (mCallback == null) {
                        Logger.w("ScreenRecordService.callback is null...");
                    } else {
                        int size = mFloatView == null ? 0 : mFloatView.mShotArray == null ? 0 : mFloatView.mShotArray.size();
                        mCallback.onRebindService(size, 0, "");
                    }
                } catch (RemoteException e) {
                    Logger.e(e);
                }
                return 0;
            }

            mVideoDir = dirName;
            maxRecordSecondTime = maxDuration;
            recordStartTime = AppUtils.getTimeNoMillis(serverTime);
            mIgnoreSim = ignoreSim;
            mMicMode = micMode;
            digest = fileDigest;

            //开始录屏，状态栏增加通知提高存活率
            AppUtils.startForegroundWithNotification(mService, 100, "录屏录制中");

            mHandler.post(() ->
                    mBatteryReceiver.checkCanStart(getApplicationContext(),
                        () -> FlutterScreenRecordingPlugin.mPlugin.requestMediaProjection(),
                        () -> screenFail(LOW_BATTERY)));
            return 0;
        }

        @Override
        public void listenCallback(String packageName, IFaXinScreenCallback callback) {
            mCallback = callback;
        }

        @Override
        public void stopScreen(String packageName) {
            if (isScreenRecording(packageName)) { //只允许当前在使用的app，能结束录屏
                mHandler.post(() -> stopRecord(true, 0));
            }
        }

        @Override
        public boolean isScreenRecording(String packageName) {
            return isRecordRunning;
        }
    };

    private static ScreenRecordService mService;
    private IFaXinScreenCallback mCallback;

    public static void screenFail(int code) {
        if (mService == null) {
            Logger.w("ScreenRecordService is null...");
            return;
        }
        try {
            if (mService.mCallback == null) {
                Logger.w("ScreenRecordService.callback is null...");
            } else {
                mService.mCallback.onCallback(code);
            }
        } catch (RemoteException e) {
            mService.handleRemoteException(e);
        }
        if (code != FaXinServiceConnection.ERR_AUTH) {
            String s = "screen fail. code : " + code;
            Logger.e(s);
            FlutterLoganPlugin.w(s, 3);
        }
        if (code != SCREEN_RECORDING) { //通知app 当前正在录屏，不能结束录屏操作
            mService.stopRecord(true, code);
        }
    }

    public static void authSuccess(MediaProjection mediaProjection) {
        if (mService != null) {
            mService.mediaProjection = mediaProjection;
            mService.prepare(mService.mMicMode == 1);
        }
    }

    static boolean isScreenRunning() {
        return mService != null && mService.isRecordRunning;
    }

    static int mp4Duration() {
        return  mService == null ? 0 : mService.getRecordDuration();
    }

    static ScreenRecordService getService() {
        return mService;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        mService = this;
        mBatteryReceiver = new BatteryBroadcastReceiver(this);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Logger.w("ScreenRecordService onBind", this.toString());
        return mStub;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        mService = null;
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        AppUtils.cancelNotification(this, 100);
        Logger.w("ScreenRecordService onDestroy", this.toString());
        super.onDestroy();
        if (isRecordRunning) {
            stopRecord(true, 0);
        }

        mService = null;
        mBatteryReceiver = AppUtils.unregisterReceiver(this, mBatteryReceiver);
        mNetworkReceiver = AppUtils.unregisterReceiver(this, mNetworkReceiver);
    }

    ///录屏时，开启了wifi，停止录屏去除录制文件
    public static final int ERR_RECORD_WIFI = 14;
    public static final int ERR_RECORD_VPN = 15;
    /**/ final Handler mHandler = new Handler();
    //    private LocationService mLocationService;
    private BatteryBroadcastReceiver mBatteryReceiver;
    private NetworkBroadcast mNetworkReceiver;
    private FloatView mFloatView;
    private MediaProjection mediaProjection;
    private MediaRecorderProxy mediaRecorder;

    /**/ VirtualDisplay videoDisplay;
    private int maxRecordSecondTime;//允许最大录屏的时间，以秒为单位
    private String mVideoDir;
    /*package*/ long localStartRecordTime, recordStartTime;
    /*package*/ String mVideoPath, digest;
    /*package*/ boolean mIgnoreSim;
    /*package*/ int mMicMode;
    private CountTimer mTimer;
    private boolean isRecordRunning;

//    private double latitude, longitude;
//    private String address;

    @SuppressLint("WrongConstant")
    private void prepare(boolean isMicOpen) {
        String name = AppUtils.formatTime("yyyyMMddHHmmss", recordStartTime);
        File mFileDir = new File(mVideoDir, "sjlp" + name);
        mFileDir.mkdirs();
        File mTempFile = new File(mFileDir, String.format("sjlp%s.mp4", name));

        WindowManager wmanager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics dm = new DisplayMetrics();
        wmanager.getDefaultDisplay().getRealMetrics(dm);
        int[] size = MediaRecorderProxy.calculeResolution(dm);
        int w = size[0];
        int h = size[1];

        videoDisplay = mediaProjection.createVirtualDisplay("Screen", w, h, dm.densityDpi,
                DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, null, null, null);

        mVideoPath = mTempFile.getPath();
        FlutterLoganPlugin.w(String.format(Locale.CHINESE, "Prepare path=%s, width=%d, height=%d", mVideoPath, w, h), 3);
        try {
            mediaRecorder = new MediaRecorderProxy(w, h, mVideoPath, maxRecordSecondTime);
            mediaRecorder.setOnErrorListener((r, code, e) -> screenFail(code));
            mediaRecorder.prepare(isMicOpen, videoDisplay);
        } catch (IOException e) {
            String ee = Logger.getStackTraceString(e);
            Logger.e(ee);
            FlutterLoganPlugin.w(ee, 3);
            screenFail(ERR_INIT);
            return;
        }

        isRecordRunning = true;
        mFloatView = new FloatView(this);
        mediaRecorder.start();

        localStartRecordTime = System.currentTimeMillis();
        mTimer = new CountTimer() {
            @Override
            protected void result(int t) {
                int left = (int) (maxRecordSecondTime - t);
                if (left > 0 && left < 60 && left % 5 == 0) { //5秒提示一次
                    int h = t / 3600;
                    int m = (int) (t % 3600 / 60);
                    int s = (int) (t % 60);
                    String str = String.format(Locale.CHINESE, "当前已录制%d时%d分%d秒，剩余%d秒", h, m, s, left);
                    AppUtils.showToast(getApplicationContext(), str);
                } else if (left <= 1) { //时间到
                    screenFail(ERR_DURATION);
                }
                int dur = getRecordDuration();
                String duration = CountTimer.getFormat(dur);
                if (mFloatView != null) mFloatView.updateTime(duration);
                try {
                    if (mCallback != null) mCallback.onDuration(dur, duration);
                } catch (RemoteException e) {
                    handleRemoteException(e);
                }
                mBatteryReceiver.checkBattery(ScreenRecordService.this, () -> screenFail(LOW_BATTERY), () -> {});
            }
        };
        mTimer.schedule(maxRecordSecondTime, 1);

        showCheckDialog(mTempFile.getParent());
    }

    void stopRecord(boolean callbackText, int code) {
        Logger.w("screen stop ... code: " + code);
        MediaRecorderProxy.resetVolume(this);
        if(mTimer != null) mTimer.stop();
        isRecordRunning = false;
        if (mFloatView != null) {
            mFloatView.removeFloatView();
        }
        if (mediaRecorder != null) {
            mediaRecorder.stop();
            mediaRecorder = null;

            videoDisplay.setSurface(null);
            videoDisplay.release();
            videoDisplay = null;
        }
        if (mediaProjection != null) {
            mediaProjection.stop();
        }
        mediaProjection = null;
        stopForeground(true);

//        if(mLocationService != null) mLocationService.stopLocation();
        if (callbackText && code != CHECK_FAIL) backVideoInfo(code);
        stopSelf();
    }

    //视频正常结束录制，将视频信息返回
    private void backVideoInfo(int code) {
        try {
            if (TextUtils.isEmpty(mVideoPath)) {
                stopRecordToFlutter("", code);
                RecordServiceConnection.sendLog( mCallback, "video path is null");
                return;
            }
            File file = new File(mVideoPath);
            if (!file.exists()) {
                stopRecordToFlutter("", code);
                RecordServiceConnection.sendLog( mCallback, "video file no exits. " + mVideoPath);
                return;
            }
            if (file.length() == 0) {
                stopRecordToFlutter("", code); //此处不能用screenFail()，不然会造成死循环
                RecordServiceConnection.sendLog( mCallback, "video file is empty. " + mVideoPath);
                return;
            }
            
            JSONStringer jsonStringer = new JSONStringer();
            jsonStringer.object()
                    .key("micStatus").value(mMicMode)
                    .key("file").value(mVideoPath)
                    .key("fileSize").value(file.length())
                    .key("name").value(file.getName())
                    .key("startTime").value(getRecordStartTime())
                    .key("endTime").value(AppUtils.getTimeNoMillis(getRecordEndTime()))
                    .key("array").value(EvidShotBean.transfor(mFloatView.mShotArray));

            File reportFile = new File(file.getParent(), "/report.txt");
            jsonStringer.key("report").value(EvidShotBean.transfor(reportFile, digest));
            jsonStringer.endObject();

            stopRecordToFlutter(jsonStringer.toString(), code);
        } catch (Exception e) {
            RecordServiceConnection.sendLog(mCallback, e);
        }
    }

    private void stopRecordToFlutter(String txt, int code) throws RemoteException {
        if (mCallback != null) mCallback.onStopRecord(txt, code);
    }

    //获取当前视频已录制的时间
    private int getRecordDuration() {
        long time = (System.currentTimeMillis() - localStartRecordTime) / 1000;
        return (int) time;
    }

    long getRecordStartTime() {
        return recordStartTime;
    }

    private long getRecordEndTime() {
        return PathProviderExPlugin.getVideoDuration(mVideoPath) + recordStartTime;
    }

    private void showCheckDialog(String dirName) {
        showCheckDialog(dirName, this, () -> {
            if(isScreenRecording("")) {
                try {
                    File file = new File(mVideoPath);
                    if (mCallback != null) mCallback.onStartRecording(file.getParentFile().getPath());
                } catch (RemoteException e) {
                    Logger.e(e);
                }
                AppUtils.showToast(this, "屏幕录制操作开始");
                MediaRecorderProxy.changeVolume(this);
                //录制开启后，才监听网络变化，录制之前不监听防止未开始录制，就调用录制结束方法
                if (!mIgnoreSim) {
                    mNetworkReceiver = new NetworkBroadcast(this);
                }
            } else {
                screenFail(CHECK_FAIL);
            }
        });
    }

    private void showCheckDialog(String dirName, ScreenRecordService mService, Runnable runnable) {
        CheckDialog.OnCheckListener listener = (dialog, success) -> {
            if (!success) {
                mService.stopRecord(false, 0);
                File file = new File(mService.mVideoPath);
                FlutterLoganPlugin.w("Screend Check Fail." + mService.mVideoPath,3);
                AppUtils.deleteChildFiles(file.getParentFile(), true);
            }
        };
        Dialog dialog = new CheckDialog(mService, dirName, mService.mIgnoreSim).setOnCheckListener(mService.getRecordStartTime(), listener);
        dialog.setOnDismissListener((d) -> runnable.run());
        dialog.show();
    }

    /**
     * 这个用来判断 第三方app是否在录屏
     *
     * @param packageName
     */
    private boolean isScreenRecording(String packageName) {
        try {
            return mStub.isScreenRecording(packageName);
        } catch (RemoteException e) {
            return false;
        }
    }

    /**/ File getSaveBitmapFile(long time) {
        int i = mVideoPath.lastIndexOf('/') + 1;
        time = time - localStartRecordTime + recordStartTime; /* 换算成服务端的时间 */
        File file = new File(String.format("%s%s.jpg", mVideoPath.substring(0, i), AppUtils.formatTime("yyyyMMddHHmmssSSS", time)));
        return file;
    }

    /**/ void onTakeShotOk(int size) {
        if (mCallback == null) return;
        try {
            mCallback.onTakeShotOk(size);
        } catch (RemoteException e) {
            RecordServiceConnection.sendLog(mCallback, e);
        }
    }

    /**/ void moveTaskToFront() {
        if (mCallback == null) return;
        try {
            mCallback.moveTaskToFront("请点击‘结束录屏’按钮，结束录屏工作");
        } catch (RemoteException e) {
            RecordServiceConnection.sendLog(mCallback, e);
        }
    }

    /**移除截图*/
    /**/boolean removeShot(File shotFile){
        if(mFloatView == null) return false;
        return mFloatView.removeShot(shotFile);
    }

    private void handleRemoteException(RemoteException e) {
        RecordServiceConnection.sendLog(mCallback, e);
        stopRecord(false, SCREEN_Err);
    }

    private static class NetworkBroadcast extends BroadcastReceiver {
        public NetworkBroadcast(Context context) {
            context.registerReceiver(this, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent == null ? null : intent.getAction();
            Logger.d("screen record，NetworkBroadcast", action);
            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
                if (AppUtils.getNetworkType(context) == ConnectivityManager.TYPE_WIFI) {
                    Logger.d("terminate screen record，for reason wifi open");
                    ScreenRecordService.screenFail(ERR_RECORD_WIFI);
                    return;
                }
                if (CheckDialog.isVpnUsed()) {
                    Logger.d("terminate screen record ，for reason vpn open");
                    ScreenRecordService.screenFail(ERR_RECORD_VPN);
                }
            }
        }
    }
}
